#include <stdlib.h>
#include "lib.h"
#include "csranks.h"
#include "cardmgr.h"
#include "CHoldemHand.h"
#include "memory.h"




///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::CHoldemHand(CardMgr* Mgr, Hand *pCommunity)
///
/// @param  Mgr             (in)  pointer to global card manager object
////@param  pCommunity      (in)  pointer to community cards that both dealer and player share.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
CHoldemHand::CHoldemHand(CardMgr* Mgr, Hand *pCommunity) : Hand(Mgr), m_bestHand(Mgr)
{
    m_byHighCard = 0;
    m_nHandRank = 0xff;
    m_pCommunity = pCommunity;
}



///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::checkFourOfAKind(Hand& workingHand, uint8 abyRankCounts[13])
///
/// Check to see if there's 4 of a kind in the available cards.
///
/// @param  workingHand     (in) hand with both the community and non-community cards.
/// @param  abyRankCounts   (in) array containing count for each rank of card, see header.
///
/// @return bool    true if the working hand contains 4 of a kind
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CHoldemHand::checkFourOfAKind(Hand& workingHand, uint8 abyRankCounts[13])
{
    bool bRetVal = false;

    // see if any of the ranks have a count of 4.
    unsigned char *p4 = (unsigned char *) memchr(abyRankCounts, 4, 13);

    if(p4 != NULL)
    {
        // we do have 4 of a kind, figure out the rank
        int nRank = p4 - abyRankCounts;
        nRank += 2;

        // record results
        m_nHandRank = HR_4OFAKIND;
        bRetVal = true;        

        // now, copy all cards of that rank from workingHand to m_bestHand
        int nWorkCnt = workingHand.getCount();

        for (int nI = 0 ; nI < nWorkCnt; nI++)
        {
            uint8 nCard = workingHand.getCardAt(nI);

            if(mgr->getRank(nCard)==nRank)
            {
                m_bestHand.add(nCard);
            }
        }

        // okay, we need one more card to make a full 5 cards for the "best" hand.
        // get the highest card that's not part of the 4 of a kind.
        for(nI=12 ;nI>=0;nI--)
        {
            if(abyRankCounts[nI]>0 && abyRankCounts[nI]!=4) // make sure you don't copy one of the 4 cards again...
            {
                nRank = nI + 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.

                for(int nX=0;nX<nWorkCnt;nX++)
                {
                    uint8 nCard = workingHand.getCardAt(nX);

                    if(mgr->getRank(nCard)==nRank)
                    {
                        m_bestHand.add(nCard);

                        break;  // only need one card.
                    }
                }

                break;  // only needed one card...exit loop.
            }
        }
    }

    return bRetVal;
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     checkStraightFlush(Hand& workingHand, int nSuit, uint8 abyRankCounts[13])
///
/// Check to see if there's a straight flush in the available cards.
///
/// @param  workingHand     (in) hand with both the community and non-community cards.
/// @param  nSuit           (in) the flush suit.
/// @param  abyRankCounts   (in) array containing count for each rank of card, see header.
///
/// @return bool    true if the working hand contains a straight flush
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CHoldemHand::checkStraightFlush(Hand& workingHand, int nSuit, uint8 abyRankCounts[13])
{
    bool bStraightFlush = false;

    // so start working our way from the highest rank to the lowest rank looking
    // for 5 consecutive cards of the same suit.
    for(int nJ=12;nJ>2;nJ--)
    {
        m_bestHand.restore(workingHand.getCards(),0);   // reset this.

        int nRank;
        int nStop = nJ-5;  // note that this can give a -2...that's for ace low.

        // starting at current rank, look at the counts of it and the next 4 lower 
        // ranks to see if we have any cards of those ranks.
        for(int nK=nJ;nK>nStop;nK--) 
        {                    
            if(nK==-1)
            {
                // handle ace as "1".
                nRank = 14;
            }
            else
            {
                nRank = nK+2; // rank is always 2 more than array offset since rank of "2" is at array offset 0.
            }

            if(abyRankCounts[nRank-2]==0) // do we have any cards of this rank?
            {
                // Nope.  This breaks the straight, break out of the loop.  
                break;
            }
            else
            {
                // okay, we're still in the money on getting a straight.

                // now we want to look through the actual cards in the working hand
                // and see if there's a card of this rank that is of the right suit
                // to make this a straight flush.
                int nWorkCnt = workingHand.getCount();

                bool bSuitMatch=false;

                for(int nX=0;nX<nWorkCnt;nX++)
                {
                    uint8 nCard = workingHand.getCardAt(nX);

                    if(mgr->getRank(nCard)==nRank &&
                       ((mgr->getSuit(nCard) >> 4)-1)==nSuit)
                    {
                        // we found a card of the right suit AND the right
                        // rank to continue the straight flush!  
                        bSuitMatch=true;

                        // go ahead and add it to the "best" hand...we can erase it later if things don't work out.
                        m_bestHand.add(nCard);  
                        
                        break;  
                    }
                }

                if(bSuitMatch==false)
                {
                    // nope...wrong suit, this straight flush is broken,
                    // abandon ship.
                    break;
                }
            }
        }

        if(nK==nStop)
        {   
            // Aha!  We made it through all 5 cards with no rejects!
            // we have a straight flush...stop looking.
            bStraightFlush=true;
            break;
        }
    }

    if(bStraightFlush==true)
    {
        if(nJ==12)
        { 
            // it's Ace High...
            m_nHandRank = HR_ROYALFLUSH;
        }
        else
        {
            m_nHandRank = HR_STRAIGHTFLUSH;
        }
    }

    return bStraightFlush;
}




///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     checkFlush(Hand& workingHand, uint8 abyRankCounts[13], uint8 abySuitCounts[4])
///
/// Check to see if there's a flush in the available cards.
///
/// @param  workingHand     (in) hand with both the community and non-community cards.
/// @param  abyRankCounts   (in) array containing count for each rank of card, see header.
/// @param  abySuitCounts   (in) array containing count for each suit.
///
/// @return bool    true if the working hand contains a flush
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CHoldemHand::checkFlush(Hand& workingHand, uint8 abyRankCounts[13], uint8 abySuitCounts[4])
{
    bool bFlush = false;

    // look though our suit counts and see if we have 5 or more cards of any one suit.
    for(int nI=0;nI<4;nI++)
    {
        if(abySuitCounts[nI]>4)
        {
            // we have AT LEAST 5 of this suit...let's see if it's a straight flush...
            bFlush = checkStraightFlush(workingHand, nI, abyRankCounts);
            
            if(!bFlush)
            {
                // nope, it's just a regular flush.
                int nWorkCnt = workingHand.getCount();

                // clear the best hand.
                m_bestHand.restore(workingHand.getCards(),0);

                // go through the cards in the working hand and add all of them
                // that are of the flush suit to the "best" hand.
                for(int nX=nWorkCnt-1;nX>=0;nX--)
                {
                    uint8 nCard = workingHand.getCardAt(nX);

                    if(((mgr->getSuit(nCard) >> 4)-1)==nI)
                    {
                        m_bestHand.add(nCard);
                    }
                }
                
                // how many cards do we have?  Theoretically, all cards
                // could have been of the same suit.
                int nWinCount = m_bestHand.getCount();

                // NOW, remove lowest ranking cards until we're back down to 5.
                while(nWinCount > 5)    
                {
                    int nMinRank = mgr->getRank(m_bestHand.getCardAt(0));
                    int nMinNdx  = 0;

                    // find the lowest card
                    for(nX=1;nX<nWinCount;nX++)
                    {
                        int nRank = mgr->getRank(m_bestHand.getCardAt(nX));

                        if(nRank<nMinRank)
                        {
                            nMinRank = nRank;
                            nMinNdx = nX;
                        }
                    }

                    // build a new hand without the low card
                    Hand tempHand(mgr);

                    for(nX=0;nX<nWinCount;nX++)
                    {
                        if(nMinNdx!=nX) // leave out low card.
                        {
                            tempHand.add(m_bestHand.getCardAt(nX));
                        }
                    }

                    // copy the new hand sans low card over the old hand.
                    m_bestHand.restore(tempHand.getCards(), tempHand.getCount());

                    // since we removed the lowest card, decrement the count by 1.
                    nWinCount--;
                }

                // record results.
                m_nHandRank = HR_FLUSH;
                bFlush = true;
                break;  // DONE...break out of loop and return to caller.
            }
        }
    }

    return bFlush;
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::checkStraight(Hand& workingHand, uint8 abyRankCounts[13])
///
/// Check to see if there's a straight in the available cards.
///
/// @param  workingHand     (in) hand with both the community and non-community cards.
/// @param  abyRankCounts   (in) array containing count for each rank of card, see header.
///
/// @return bool    true if the working hand contains a straight
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CHoldemHand::checkStraight(Hand& workingHand, uint8 abyRankCounts[13])
{
    bool bStraight = false;

    // Start at the highest rank and check for 5 consecutive ranks that have a non-zero count
    for(int nJ=12;nJ>2;nJ--)
    {
        int nRank = 0;

        // Look at the current rank and the 4 ranks below it.
        int nStop = nJ-5;   
        for(int nK=nJ;nK>nStop;nK--)
        {
            // convert index into rank
            if(nK==-1)
            {
                // handle ace low straight.
                nRank = 14;
            }
            else
            {
                nRank = nK+2;   // rank is always 2 more than array offset since rank of "2" is at array offset 0.
            }

            // check card count for this rank
            if(abyRankCounts[nRank-2]==0)   
            {
                // The working hand has no cards of this rank.  
                // the straight is broken.  Next.
                break;
            }
        }
        
        // did we make it without breaking the straight???
        if(nK==nStop)
        {        
            // we have a straight...

            int nWorkCnt = workingHand.getCount();

            m_bestHand.restore(workingHand.getCards(),0);   // make sure that this is empty.

            // okay, go through the 5 ranks, find the matching card in the working hand
            // and copy that card to the "Best" hand.
            for(nK=nJ;nK>nStop;nK--)
            {
                // convert index into rank
                if(nK==-1)
                {
                    // handle ace as "1".
                    nRank = 14;
                }
                else
                {
                    nRank = nK+2;   // rank is always 2 more than array offset since rank of "2" is at array offset 0.
                }


                // look through the working hand
                for(int nX=0;nX<nWorkCnt;nX++)
                {
                    uint8 nCard = workingHand.getCardAt(nX);

                    // is this the card that matches the current rank?
                    if(mgr->getRank(nCard)==nRank)
                    {
                        // yep, add this card to the "best" hand.
                        m_bestHand.add(nCard);
                        break;
                    }
                }

            }

            m_nHandRank = HR_STRAIGHT;
            bStraight = true;
            break;
        }
    }

    return bStraight;
}




///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::checkThreeOfAKind(Hand& workingHand, uint8 abyRankCounts[13])
///
/// Check to see if there's 3 of a kind in the available cards.  Also check for full house.
///
/// @param  workingHand     (in) hand with both the community and non-community cards.
/// @param  abyRankCounts   (in) array containing count for each rank of card, see header.
///
/// @return bool    true if the working hand contains 3 of a kind
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CHoldemHand::checkThreeOfAKind(Hand& workingHand, uint8 abyRankCounts[13])
{            
    bool bThree = false;
    int n3Ndx = 0;

    // look through the ranks for a count of 3 or more (more would have been caught by 4 of a kind check, but anyway).
    for(int nI=12;nI>=0;nI--)
    {
        if(abyRankCounts[nI]>2)
        {
            bThree = true;
            n3Ndx = nI;
            break;
        }
    }

    if(bThree==true)
    {
        // aha!  We have 3 of a kind!

        int nWorkCnt = workingHand.getCount();

        m_bestHand.restore(workingHand.getCards(),0);   // clear this out.

        int nRank  = n3Ndx + 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.
        int nCount = 0;

        // copy the 3 cards of the current rank from the working hand to 
        // the "best" hand.
        for(int nX=0;nX<nWorkCnt && nCount<3;nX++)
        {
            uint8 nCard = workingHand.getCardAt(nX);

            if(mgr->getRank(nCard)==nRank)
            {
                m_bestHand.add(nCard);
                nCount++;
            }
        }

        // NOW, let's check for a pair...to see if we have a full house.
        bool bTwo = false;
        int n2Ndx = 0;
        for(nI=12 ;nI>=0;nI--)
        {
            // if it has at least 2 cards of this rank, and isn't the 3 of a kind rank
            if(abyRankCounts[nI]>1 && nI!=n3Ndx) 
            {
                // A hit!  We have a full house!
                bTwo = true;
                n2Ndx = nI;
                break;
            }
        }

        if(bTwo==true)
        {
            // copy the pair of cards from the working hand to the "best" hand.

            nRank  = n2Ndx + 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.
            nCount = 0;

            for(int nX=0;nX<nWorkCnt && nCount<2;nX++)
            {
                uint8 nCard = workingHand.getCardAt(nX);

                if(mgr->getRank(nCard)==nRank)
                {
                    m_bestHand.add(nCard);
                    nCount++;   // we only want 2...
                }
            }

            // record result
            m_nHandRank = HR_FULLHOUSE;
        }
        else
        {
            // hmmm.  Okay, we only have 3 of a kind.  Copy over the next 2
            // highest cards so as to round out the "best" hand with 5 cards.
            nCount = 0;

            for(nI=12 ;nI>=0 && nCount<2;nI--)
            {
                if(abyRankCounts[nI]==1) // all that's left will be cards with a count of 1...so copy the highest 2 of those.
                {
                    nRank = nI + 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.
                    for(int nX=0;nX<nWorkCnt && nCount<2;nX++)
                    {
                        uint8 nCard = workingHand.getCardAt(nX);

                        if(mgr->getRank(nCard)==nRank)
                        {
                            m_bestHand.add(nCard);
                            nCount++;   // we only want 2 cards...
                        }
                    }
                }
            }

            // record result
            m_nHandRank = HR_3OFAKIND;
        }
    }

    return bThree;
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::checkPairs(Hand& workingHand, uint8 abyRankCounts[13])
///
/// Check to see if there's 1 or 2 pairs in the available cards.
///
/// @param  workingHand     (in) hand with both the community and non-community cards.
/// @param  abyRankCounts   (in) array containing count for each rank of card, see header.
///
/// @return bool    true if the working hand contains 4 of a kind
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CHoldemHand::checkPairs(Hand& workingHand, uint8 abyRankCounts[13])
{
    bool bPair = false;
    int nPairNdx = 0;

    // start with the highest rank and look for any rank that
    // we have more than one of.
    for(int nI=12;nI>=0;nI--)
    {
        if(abyRankCounts[nI]>1)
        {
            bPair = true;
            nPairNdx = nI;
            break;
        }
    }

    if(bPair==true)
    {
        // found a pair!

        int nWorkCnt = workingHand.getCount();

        m_bestHand.restore(workingHand.getCards(),0); // clear this out.

        int nRank  = nPairNdx + 2;      // rank is always 2 more than array offset since rank of "2" is at array offset 0.

        // copy the cards of this pair rank from the working hand 
        // to the "best" hand.
        for(int nX=0;nX<nWorkCnt;nX++)
        {
            uint8 nCard = workingHand.getCardAt(nX);

            if(mgr->getRank(nCard)==nRank)
            {
                m_bestHand.add(nCard);
            }
        }


        // okay, now let's see if we have a second pair:
        bool bPair2 = false;
        int nPairNdx2 = 0;

        // continue looping down through rank counts,
        for(nI=nPairNdx-1;nI>=0;nI--)
        {
            if(abyRankCounts[nI]>1)
            {
                // found ANOTHER pair!
                bPair2 = true;
                nPairNdx2 = nI;
                break;
            }
        }

        if(bPair2==true)
        {
            // okay copy second pair to the "best" hand.

            nRank  = nPairNdx2 + 2; // rank is always 2 more than array offset since rank of "2" is at array offset 0.

            for(int nX=0;nX<nWorkCnt;nX++)
            {
                uint8 nCard = workingHand.getCardAt(nX);

                if(mgr->getRank(nCard)==nRank)
                {
                    m_bestHand.add(nCard);
                }
            }

            // record result
            m_nHandRank = HR_2PAIR;
        }
        else
        {
            // only 1 pair...didn't find a second pair.
            // record result
            m_nHandRank = HR_PAIR;
        }

        // okay, we need 5 cards to make a full "best" hand, so now
        // we need to copy in enough extra cards to do this.

        int nAdd = (bPair2==true)?1:3;      // how many extra cards do we need?
        int nHigh = (bPair2==true)?nPairNdx2:nPairNdx;    // make sure we don't recopy one of our pair cards...
                
        int nCount = 0;
        // starting with the highest rank, look for cards that we can copy into our "best" hand
        for(nI=12;nI>=0 && nCount<nAdd;nI--)
        {
            // IF we have one card of this rank, OR we have more than one and it's not one of our pair cards
            if(abyRankCounts[nI]==1 || (abyRankCounts[nI]>1 && nI<nHigh))
            {
                // THEN copy that bastard over.

                nRank = nI + 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.

                // of course, to copy it over, we have to find it in the working hand...
                for(int nX=0;nX<nWorkCnt;nX++)
                {
                    uint8 nCard = workingHand.getCardAt(nX);

                    if(mgr->getRank(nCard)==nRank)
                    {
                        // this is our card...copy it.
                        m_bestHand.add(nCard);

                        // we only want 1 or 3 cards, so keep count
                        nCount++;


                        break;
                    }
                }
            }
        }        
    }

    return bPair;
}







///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::processHighCard(Hand& workingHand, uint8 abyRankCounts[13])
///
/// Figure out the 5 best cards by rank out of the available cards.
///
/// @param  workingHand     (in) hand with both the community and non-community cards.
/// @param  abyRankCounts   (in) array containing count for each rank of card, see header.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void CHoldemHand::processHighCard(Hand& workingHand, uint8 abyRankCounts[13])
{
    int nCount=0;

    int nWorkCnt = workingHand.getCount();

    // apparently we don't have anything good, so just copy the
    // 5 highest cards that we do have.
    for(int nI=12;nI>=0 && nCount<5;nI--)
    {
        if(abyRankCounts[nI]==1)    // we have a card of this rank?
        {
            int nRank = nI + 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.

            // find the card of the current rank in the working
            // hand and copy it to the "best" hand.
            for(int nX=0;nX<nWorkCnt;nX++)
            {
                uint8 nCard = workingHand.getCardAt(nX);

                if(mgr->getRank(nCard)==nRank)
                {
                    // copy the card to the "best" hand.
                    m_bestHand.add(nCard);

                    // we only want 5 cards, so keep count of how 
                    // many we've copied so far.
                    nCount++;
                    break;
                }
            }
        }
    }

    // record result.
    m_nHandRank = HR_HIGHCARD;
}




///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::rankHand()
///
/// This function examines the available cards and pulls out the best 5 card hand
///
/// @param  bUseRiver - if true use all 5 community cards, if false only use the first 3 community cards.
///
/// @return bool8    currently always returns true...hmmmm...
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool8 CHoldemHand::rankHand(bool bUseRiver)
{    

    // first move the community cards and regular cards into the same hand.
    Hand workingHand(mgr);

    workingHand.add(this->getCardAt(0));
    workingHand.add(this->getCardAt(1));

    int nCommCnt = m_pCommunity->getCount();

    if(bUseRiver == false)
    {
        // last minute addition...for progressives, we only want to use the player's
        // two cards, plus the first 3 community cards...DON'T use all 5 community cards
        // when figuring "progressive wins".
        nCommCnt = 3;
    }

    for(int nI=0;nI<nCommCnt;nI++)
    {
        workingHand.add(m_pCommunity->getCardAt(nI));
    }

    // go ahead and clear the "best" hand.
    m_bestHand.restore(workingHand.getCards(),0);

    // zero out the rank counts.
    memset(m_abyRankCounts, 0, sizeof(m_abyRankCounts));

    // zero out the suit counts
    uint8 abySuitCounts[4];
    memset(abySuitCounts, 0, sizeof(abySuitCounts));
    
    // okay now step through the cards in our working hand and
    // count up how many we have of each rank and how many of 
    // each suit.
    int nRank=0;

    int nWorkCnt = workingHand.getCount();
    for (int i = 0 ; i < nWorkCnt; i++)
    {
        nRank = mgr->getRank(workingHand.getCardAt(i));

        if (nRank >= 2)
        {
            // increment rank count
            ++m_abyRankCounts[nRank - 2];   // rank is always 2 more than array offset since rank of "2" is at array offset 0.

            // increment suit count.
            abySuitCounts[(mgr->getSuit(workingHand.getCardAt(i)) >> 4)-1]++;
        }
    }

    // do we have 4 of a kind?
    if(checkFourOfAKind(workingHand, m_abyRankCounts))
    {
        // yep, we're done.
        return true;
    }

    // do we have a royal flush, a straight flush, or a plain flush?
    if(checkFlush(workingHand, m_abyRankCounts, abySuitCounts))
    {
        // yep, we're done.
        return true;
    }

    // do we have a straight?
    if(checkStraight(workingHand, m_abyRankCounts))
    {
        // yep, we're done.
        return true;
    }

    // do we have 3 of a kind or a full house?
    if(checkThreeOfAKind(workingHand, m_abyRankCounts))
    {
        // yep, we're done.
        return true;
    }

    // how about pairs?  Do we have one or two pairs?
    if(checkPairs(workingHand, m_abyRankCounts))
    {
        // yep, we're done.
        return true;
    }

    // hmmmm...looks like we have none of the above, so just
    // pick out the highest 5 cards.
    processHighCard(workingHand, m_abyRankCounts);

    return true;
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::getRank()
///
/// @return int32  returns the rank of the "best" hand, as calculated by calling the rankHand method.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
int32 CHoldemHand::getRank()
{
    return m_nHandRank;
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::getHighCard()
///
/// @return uint8    returns the highest card that was calculated by the last 
//                   call to "calcNthHighest".
///
///////////////////////////////////////////////////////////////////////////////////////////////////
uint8 CHoldemHand::getHighCard()
{
    return m_byHighCard;
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::qualifies()
///
/// @return bool8       returns true if this hand meets the minimum requirements needed for the
///                     dealer to continue...currently a pair of 4's.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool8 CHoldemHand::qualifies()
{
    bool bRetVal = false;

    
    if(m_nHandRank > HR_PAIR)
    {
        // everything greater than a single pair automatically qualifies
        bRetVal = true;
    }
    else if(m_nHandRank > HR_HIGHCARD)
    {
        // so we have a pair...see if it's 4's or better.

        int nRank = ((uint8 *)memchr(m_abyRankCounts,2,13)) - m_abyRankCounts;

        nRank += 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.

        if(nRank>=4)
        {
            // yep, we qualify.
            bRetVal=true;
        }
    }

    return bRetVal;
}




///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     int32 CHoldemHand::outrank(uint8* Buf)
///
/// Encodes the rank of the best 5 card hand into Buf.
///
/// @param  Buf     The buffer we want to store our rank in.
///
/// @return int32   always returns 1...the amount of data written to the buffer.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
int32 CHoldemHand::outrank(uint8* Buf)
{
    *Buf = (uint8)(m_nHandRank & 0xff);
    return 1;
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::outhigh(uint8* Buf)
///
/// Encodes the deciding high card from the best 5 card hand into Buf.
///
/// @param  Buf     The buffer we want to store our highcard value in.
///
/// @return int32   the amount of data written to the buffer
///
///////////////////////////////////////////////////////////////////////////////////////////////////
int32 CHoldemHand::outhigh(uint8* Buf)
{
    int32 used = 0;

    mgr->encode(m_byHighCard, Buf, &used);

    return used;
}




///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::calcNthHighest(int32 n)
///
/// Looks at the best hand and figures out the Nth highest card in that hand
///
/// @param  n   1st, 2cnd, 3rd, 4th, or 5th highest card.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void CHoldemHand::calcNthHighest(int32 n)
{
    // zero out the rank counts array
    memset(m_abyRankCounts, 0, sizeof(m_abyRankCounts));

    // count how many of each rank we have in our "best" hand.
    int nCnt = m_bestHand.getCount();
    for (int nI = 0 ; nI < nCnt; nI++)
    {
        int nRank = mgr->getRank(m_bestHand.getCardAt(nI));

        if (nRank >= 2)
        {
            ++m_abyRankCounts[nRank - 2];   // rank is always 2 more than array offset since rank of "2" is at array offset 0.
        }
    }

    // get the Nth highest card.
    m_byHighCard = getNthHighest(n, m_nHandRank);
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CHoldemHand::getNthHighest(int32 n, int32 r)
///
/// Used by calcNthHighest to do the real work of figuring out the Nth highest card in that hand.
///
/// @param  n   1st, 2cnd, 3rd, 4th, or 5th highest card.
/// @param  r   The rank that "n" should be evaluated within
///
/// @return uint8    
///
///////////////////////////////////////////////////////////////////////////////////////////////////
uint8 CHoldemHand::getNthHighest(int32 n, int32 r)
{
    uint8 *probe;

    switch (r)
    {
        case HR_4OFAKIND:
        {
            if (n < 4)
            {
                return (uint8)(((uint8*)memchr(m_abyRankCounts, 4, 13) - m_abyRankCounts) + 2);
            }

            return getNthHighest(n, HR_HIGHCARD);
        }
        case HR_3OFAKIND:
        {
            if (n < 3)
            {
                return (uint8)(((uint8*)memchr(m_abyRankCounts, 3, 13) - m_abyRankCounts) + 2);
            }

            return getNthHighest(n, HR_HIGHCARD);
        }        
        case HR_FULLHOUSE:
        {
            if (n < 3)
            {
                return getNthHighest(n, HR_3OFAKIND); 
            }

            return (uint8)(((uint8*)memchr(m_abyRankCounts, 2, 13) - m_abyRankCounts) + 2);
        }
        case HR_2PAIR:
        {
            if (n < 4)
            {
                probe = (uint8*)memchr(m_abyRankCounts, 2, 13);

                if (n < 2)
                {
                    uint8* probe2;

                    probe2 = (uint8*)memchr(probe+1, 2, 13 - (probe-m_abyRankCounts+1));

                    return (uint8)((probe2 - m_abyRankCounts) + 2);
                }

                return (uint8)((probe - m_abyRankCounts) + 2);
            }

            return getNthHighest(n, HR_HIGHCARD);
        }
        case HR_PAIR:
        {
            if (n < 2)
            {
                return (uint8)(((uint8*)memchr(m_abyRankCounts, 2, 13) - m_abyRankCounts) + 2);
            }

            return getNthHighest(n, HR_HIGHCARD);
        }
        case HR_ROYALFLUSH:
        case HR_STRAIGHTFLUSH:
        case HR_STRAIGHT:
        {
            bool8 aceLowStraight;
            int32 i;

            // if we have an ace and a "5", and we're in a straight, then we must have an
            // ace low straight.
            aceLowStraight = (bool8)(m_abyRankCounts[CardMgr::ACE-2] && m_abyRankCounts[5-2]);

            probe = m_abyRankCounts + 13;
            i = 0;

            do
            {
                while (*(--probe) == 0)
                    /* search */ ;

            } while (++i < n);

            if (aceLowStraight)
            {
                if (n < 4)
                {
                    return (uint8)(5 - n);            // 5, then 4, then 3, then 2
                }
                else
                {
                    return (uint8)(CardMgr::ACE-2);    // finally, ACE
                }
            }

            return (uint8)((probe - m_abyRankCounts) + 2);
        }

        case HR_FLUSH:
        case HR_HIGHCARD:
        default:
        {
            int32 whichSingle = 4 - n;
            uint8* probe2;

            probe = (uint8*)memchr(m_abyRankCounts, 1, 13);
            while (whichSingle--)
            {
                probe2 = (uint8*)memchr(probe+1, 1, 13 - (probe-m_abyRankCounts+1));
                probe = probe2;
            }

            return (uint8)((probe - m_abyRankCounts) + 2);
        }
    }
}


